﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace Djs.Common.Components
{
    public class GEditableVirtualArea : GEditableArea, IInteractiveItem
    {
        #region Virtual item memebers (constructor, VirtualBounds and ActiveBounds, validity check and calculators)
        public GEditableVirtualArea(IVirtualConvertor virtualConvertor)
        {
            this._VirtualConvertor = virtualConvertor;
        }
        /// <summary>
        /// Virtual convertor
        /// </summary>
        public IVirtualConvertor VirtualConvertor
        {
            get { return this._VirtualConvertor; }
            set { this._VirtualConvertor = value; this.InvalidateActiveBounds(); }
        }
        private IVirtualConvertor _VirtualConvertor;
        public override Rectangle Bounds
        {
            get { this.CheckActiveBoundsValidity(false, false); return base.Bounds; }
            set { base.Bounds = value; this._CalcVirtualBounds(); }
        }
        public override Rectangle BoundsDrag
        {
            get { this.CheckActiveBoundsValidity(false, false); return base.BoundsDrag; }
            set { base.BoundsDrag = value; this._CalcVirtualBounds(); }
        }
        /// <summary>
        /// Virtual bounds (logical value).
        /// It is basical value.
        /// </summary>
        public RectangleD VirtualBounds
        {
            get { return this._VirtualBounds; } 
            set { this._VirtualBounds = value; this.InvalidateActiveBounds(); }
        }
        private RectangleD _VirtualBounds;
        /// <summary>
        /// Bounds, where item is mouse-interactive, in coordinates of parent control (GInteractiveControl / VirtualContainer).
        /// This ActiveBounds are coordinates for MouseDrag and base coordinate system for this.SubItems (its relative bounds in IInteractiveSubItem.RelativeActiveBounds).
        /// </summary>
        public override Rectangle ActiveBounds
        {
            get { this.CheckActiveBoundsValidity(false, false); return base.ActiveBounds; }
            set { base.ActiveBounds = value; this._CalcVirtualBounds(); }
        }
        /// <summary>
        /// Store value to this._VirtualBounds from base.ActiveBounds (via this._VirtualConvertor).
        /// Set this._LastIdentity (from this._VirtualConvertor.Identity).
        /// </summary>
        private void _CalcVirtualBounds()
        {
            if (this._VirtualConvertor == null || !this._VirtualConvertor.ConvertIsReady) return;
            this._VirtualBounds = this._VirtualConvertor.ConvertToLogicalFromPixel(base.Bounds).Value;
            this._LastIdentity = this._VirtualConvertor.Identity;
        }
        /// <summary>
        /// Check for _BoundsIsValid: if valid, then does nothing.
        /// If invalid: Store value to base.ActiveBounds from this._VirtualBounds via this._VirtualConvertor.
        /// Set this._LastIdentity (from this._VirtualConvertor.Identity).
        /// </summary>
        protected void CheckActiveBoundsValidity(bool needDraw, bool callEvent)
        {
            if (this._BoundsIsValid) return;

            Rectangle? bounds = this._VirtualConvertor.ConvertToPixelFromLogical(this._VirtualBounds);
            if (bounds.HasValue)
            {
                this._LastIdentity = this._VirtualConvertor.Identity;
                this.SetBounds(bounds.Value, false, needDraw, callEvent);
            }
        }
        /// <summary>
        /// Last identity of virtual convertor
        /// </summary>
        private IComparable _LastIdentity;
        /// <summary>
        /// true when this.ActiveBounds = this._VirtualConvertor.ConvertToPixelFromLogical(this.VirtualBounds)
        /// (work also with this._LastIdentity and this._VirtualConvertor.Identity)
        /// </summary>
        private bool _BoundsIsValid
        {
            get
            {
                if (this._VirtualConvertor == null || !this._VirtualConvertor.ConvertIsReady) return true;     // Without a convertor = valid
                IComparable currentIdentity = this._VirtualConvertor.Identity;
                if (currentIdentity == null) return true;
                if (this._LastIdentity == null) return false;
                return (this._LastIdentity.CompareTo(currentIdentity) == 0);
            }
        }
        /// <summary>
        /// Invalidate ActiveBounds
        /// </summary>
        public void InvalidateActiveBounds()
        {
            this._LastIdentity = null;
        }
        #endregion
    }
    /// <summary>
    /// EditableArea : area, whose position and size can interactively changed by the user (by dragging with area or edge marks).
    /// </summary>
    public class GEditableArea : InteractiveItem, IInteractiveItem
    {
        public GEditableArea()
        {
            this.DragWithGhost = true;
            this.BackColor = Color.AliceBlue;
            this.SubItemsInit();
        }
        #region Public properties and events
        /// <summary>
        /// Visual bounds of this item. On this coordinates is item displayed.
        /// Item is active on ActiveBounds, which is of 2pixel greater.
        /// </summary>
        public virtual Rectangle Bounds
        {
            get { return this._Bounds; }
            set { this.SetBounds(value, false, true, true); }
        }
        /// <summary>
        /// Visual bounds of this item. On this coordinates is item displayed.
        /// Setting value to this property does not triegger event BoundsChanged.
        /// Item is active on ActiveBounds, which is of 2pixel greater.
        /// </summary>
        public virtual Rectangle BoundsDrag
        {
            get { return this._Bounds; }
            set { this.SetBounds(value, false, false, false); }
        }
        /// <summary>
        /// Active bounds. Is about 2 pixel greater (at each direction) than Bounds.
        /// </summary>
        public override Rectangle ActiveBounds
        {
            get { return base.ActiveBounds; }
            set { this.SetBounds(value, true, true, true); }
        }
        protected virtual void SetBounds(Rectangle value, bool asActive, bool needDraw, bool callEvent)
        {
            Rectangle bounds = value;
            Rectangle activeBounds = value;
            if (asActive)
                bounds.Inflate(-ACTIVE_BORDER, -ACTIVE_BORDER);
            else
                activeBounds.Inflate(ACTIVE_BORDER, ACTIVE_BORDER);

            if (bounds == this._Bounds) return;

            this._Bounds = bounds;
            base.ActiveBounds = activeBounds;
            this.CalculateSubItemsBounds();

            if (needDraw)
                this.RepaintToLayers = GInteractiveDrawLayer.Standard;
            if (callEvent)
                this._OnBoundsChanged();
        }
        private Rectangle _Bounds;
        public event EventHandler BoundsChanged;
        private void _OnBoundsChanged()
        {
            this.OnBoundsChanged();
            if (this.BoundsChanged != null)
                this.BoundsChanged(this, EventArgs.Empty);
        }
        protected virtual void OnBoundsChanged()
        { }
        /// <summary>
        /// During drag operations show "ghost" at original coordinates
        /// </summary>
        public bool DragWithGhost { get; set; }
        /// <summary>
        /// Back color
        /// </summary>
        public Color BackColor { get; set; }
        #endregion
        #region Interactivity
        protected override void StateChanged(GInteractiveChangeStateArgs e)
        {
            EditableSubItem subItem = e.CurrentSubItem as EditableSubItem;
            switch (e.ChangeState)
            {
                case GInteractiveChangeState.MouseEnter:
                    this.CurrentSubItem = null;
                    this.RepaintToLayers = GInteractiveDrawLayer.Standard;
                    break;
                case GInteractiveChangeState.MouseLeave:
                    this.CurrentSubItem = null;
                    this.RepaintToLayers = GInteractiveDrawLayer.Standard;
                    this.RepaintAllItems = true;
                    break;
                case GInteractiveChangeState.MouseEnterSubItem:
                    this.CurrentSubItem = subItem;
                    this.RepaintToLayers = GInteractiveDrawLayer.Standard;
                    if (subItem != null)
                        e.RequiredCursorType = subItem.OverCursorType;
                    break;
                case GInteractiveChangeState.MouseLeaveSubItem:
                    this.CurrentSubItem = null;
                    this.RepaintToLayers = GInteractiveDrawLayer.Standard;
                    e.RequiredCursorType = SysCursorType.Default;
                    break;
                case GInteractiveChangeState.MouseOver:
                    this.RepaintToLayers = GInteractiveDrawLayer.Standard;
                    break;
                case GInteractiveChangeState.LeftDragBegin:
                    if (subItem != null && subItem.CanDrag)
                    {
                        this.OriginalBounds = this.Bounds;
                        this.CurrentSubItem = subItem;
                        this.RepaintAllItems = true;
                        this.RepaintToLayers = GInteractiveDrawLayer.Standard;
                        e.RequiredCursorType = subItem.DragCursorType;
                        e.UserDragPoint = this.Bounds.Location.Add(subItem.ActivePoint);
                    }
                    break;
                case GInteractiveChangeState.LeftDragMove:
                    if (this.CurrentSubItem != null && this.CurrentSubItem.CanDrag && e.UserDragPoint.HasValue)
                    {
                        this.RepaintToLayers |= GInteractiveDrawLayer.Interactive;
                        Point currentPoint = e.UserDragPoint.Value;
                        this.CalculateBoundsInteractive(this.CurrentSubItem, e.UserDragPoint.Value);
                    }
                    break;
                case GInteractiveChangeState.LeftDragEnd:
                    if (this.CurrentSubItem != null && this.CurrentSubItem.CanDrag)
                    {
                        this.RepaintAllItems = true;
                        this.OriginalBounds = null;
                        e.RequiredCursorType = subItem.OverCursorType;
                        this._OnBoundsChanged();
                    }
                    break;
                default:
                    var s = e.ChangeState;
                    break;
            }
        }
        /// <summary>
        /// Calculate new bounds during interactive moving or resizing
        /// </summary>
        /// <param name="subItem"></param>
        /// <param name="p"></param>
        protected void CalculateBoundsInteractive(EditableSubItem subItem, Point p)
        {
            Rectangle o = this.OriginalBounds.Value;
            Rectangle bd = o;
            Point b = o.Location;
            Point e = o.End();
            bool ctrl = Control.ModifierKeys == Keys.Control;
            switch (subItem.ItemType)
            {
                case MovableItemType.Area:
                    if (ctrl)
                    {
                        Point d = b.Sub(p);
                        if (Math.Abs(d.X) >= Math.Abs(d.Y))
                            p = new Point(p.X, o.Y);
                        else
                            p = new Point(o.X, p.Y);
                    }
                    bd = new Rectangle(p, o.Size);
                    break;
                case MovableItemType.TopLeft:
                    bd = DrawingExtensions.FromDim(p.X, e.X, p.Y, e.Y);
                    break;
                case MovableItemType.Top:
                    bd = DrawingExtensions.FromDim(b.X, e.X, p.Y, e.Y);
                    break;
                case MovableItemType.TopRight:
                    bd = DrawingExtensions.FromDim(b.X, p.X, p.Y, e.Y);
                    break;
                case MovableItemType.Right:
                    bd = DrawingExtensions.FromDim(b.X, p.X, b.Y, e.Y);
                    break;
                case MovableItemType.BottomRight:
                    bd = DrawingExtensions.FromDim(b.X, p.X, b.Y, p.Y);
                    break;
                case MovableItemType.Bottom:
                    bd = DrawingExtensions.FromDim(b.X, e.X, b.Y, p.Y);
                    break;
                case MovableItemType.BottomLeft:
                    bd = DrawingExtensions.FromDim(p.X, e.X, b.Y, p.Y);
                    break;
                case MovableItemType.Left:
                    bd = DrawingExtensions.FromDim(p.X, e.X, b.Y, e.Y);
                    break;
            }
            this.BoundsDrag = bd;
        }
        /// <summary>
        /// Item can be interactive moved
        /// </summary>
        protected bool CanItemMove { get { return ((this.Style & GInteractiveStyles.DragMove) != 0); } }
        /// <summary>
        /// Item can be interactive resized on X axis
        /// </summary>
        protected bool CanItemResizeX { get { return ((this.Style & GInteractiveStyles.DragResizeX) != 0); } }
        /// <summary>
        /// Item can be interactive resized on Y axis
        /// </summary>
        protected bool CanItemResizeY { get { return ((this.Style & GInteractiveStyles.DragResizeY) != 0); } }
        /// <summary>
        /// Item can be interactive resized on X and Y axis
        /// </summary>
        protected bool CanItemResize { get { return (this.CanItemResizeX && this.CanItemResizeY); } }
        /// <summary>
        /// Is enabled SubItem of specified type in current Style?
        /// </summary>
        /// <param name="itemType"></param>
        /// <returns></returns>
        protected bool IsEnabledSubItemType(MovableItemType itemType)
        {
            bool rx = this.CanItemResizeX;
            bool ry = this.CanItemResizeY;
            bool ra = this.CanItemResize;
            switch (itemType)
            {
                case MovableItemType.Area: return true;
                case MovableItemType.TopLeft: return ra;
                case MovableItemType.Top: return ry;
                case MovableItemType.TopRight: return ra;
                case MovableItemType.Right: return rx;
                case MovableItemType.BottomRight: return ra;
                case MovableItemType.Bottom: return ry;
                case MovableItemType.BottomLeft: return ra;
                case MovableItemType.Left: return rx;
            }
            return false;
        }
        /// <summary>
        /// Interactive style
        /// </summary>
        protected override GInteractiveStyles Style { get { return GInteractiveStyles.AllInteractivity | GInteractiveStyles.DragResizeX | GInteractiveStyles.DragResizeY; } }
        protected Rectangle? OriginalBounds { get; set; }
        protected MovableItemType CurrentSubItemType { get { return (this.CurrentSubItem != null ? this.CurrentSubItem.ItemType : MovableItemType.None); } }
        protected EditableSubItem CurrentSubItem { get; set; }
        #endregion
        #region Draw
        /// <summary>
        /// Draw this item as ghost
        /// </summary>
        /// <param name="e"></param>
        protected override void DrawAsGhost(GInteractiveDrawArgs e)
        {
            this.DrawGhostBackground(e);
        }
        /// <summary>
        /// Draw this item in standard mode
        /// </summary>
        /// <param name="e"></param>
        protected override void DrawStandard(GInteractiveDrawArgs e)
        {
            this.DrawStandardBackground(e);
            this.DrawClip(e);
        }
        /// <summary>
        /// Draw a background of EditableArea in standard mode
        /// </summary>
        /// <param name="e"></param>
        protected virtual void DrawStandardBackground(GInteractiveDrawArgs e)
        {
            Point? point = ((this.CurrentState == GInteractiveState.Mouse) ? this.CurrentMouseRelativePoint : (Point?)null);
            Rectangle bounds = this.Bounds;
            GPainter.DrawAreaBase(e.Graphics, bounds, this.BackColor, this.CurrentState, Orientation.Horizontal, point, null);
            GPainter.DrawString(e.Graphics, bounds, "Pozdraf", Color.Black, SystemFonts.DefaultFont, ContentAlignment.MiddleCenter);
        }
        /// <summary>
        /// Draw a background of EditableArea in Ghost mode
        /// </summary>
        /// <param name="e"></param>
        protected virtual void DrawGhostBackground(GInteractiveDrawArgs e)
        {
            if (this.OriginalBounds.HasValue)
            {
                Rectangle bounds = this.OriginalBounds.Value;
                GPainter.DrawAreaBase(e.Graphics, bounds, Color.FromArgb(80, this.BackColor), GInteractiveState.None, Orientation.Horizontal, null, null);
            }
        }
        /// <summary>
        /// Draw a editable clips of EditableArea
        /// </summary>
        /// <param name="e"></param>
        protected virtual void DrawClip(GInteractiveDrawArgs e)
        {
            // GPainter.DrawButtonBase(e.Graphics, this.Bounds, Color.GreenYellow, this.CurrentState, Orientation.Horizontal, point, null);

            if (this.CurrentState == GInteractiveState.Mouse || this.CurrentState == GInteractiveState.LeftDown || this.CurrentState == GInteractiveState.LeftDrag)
            {
                Rectangle bounds = this.Bounds.ResizeBy(0, 0, -1, -1);
                e.Graphics.DrawRectangle(Pens.Black, bounds);

                if (this.CanItemResizeX)
                {
                    e.Graphics.FillRectangle(GPainter.InteractiveClipBrushForState(this.ItemLeft.ItemState), this.ItemLeft.VisibleBounds);
                    e.Graphics.FillRectangle(GPainter.InteractiveClipBrushForState(this.ItemRight.ItemState), this.ItemRight.VisibleBounds);
                }
                if (this.CanItemResizeY)
                {
                    e.Graphics.FillRectangle(GPainter.InteractiveClipBrushForState(this.ItemTop.ItemState), this.ItemTop.VisibleBounds);
                    e.Graphics.FillRectangle(GPainter.InteractiveClipBrushForState(this.ItemBottom.ItemState), this.ItemBottom.VisibleBounds);
                }
                if (this.CanItemResize)
                {
                    e.Graphics.FillRectangle(GPainter.InteractiveClipBrushForState(this.ItemTopLeft.ItemState), this.ItemTopLeft.VisibleBounds);
                    e.Graphics.FillRectangle(GPainter.InteractiveClipBrushForState(this.ItemTopRight.ItemState), this.ItemTopRight.VisibleBounds);
                    e.Graphics.FillRectangle(GPainter.InteractiveClipBrushForState(this.ItemBottomRight.ItemState), this.ItemBottomRight.VisibleBounds);
                    e.Graphics.FillRectangle(GPainter.InteractiveClipBrushForState(this.ItemBottomLeft.ItemState), this.ItemBottomLeft.VisibleBounds);
                }
            }
        }
        #endregion
        #region SubItems, inner areas, List of sub-items
        protected void SubItemsInit()
        {
            this.SubItemDict = new Dictionary<MovableItemType, EditableSubItem>();
            int ao = ACTIVE_BORDER;
            this.SubItemDict.Add(MovableItemType.Area, new EditableSubItem(this, MovableItemType.Area, SysCursorType.Default, SysCursorType.SizeAll, ao));
            this.SubItemDict.Add(MovableItemType.TopLeft, new EditableSubItem(this, MovableItemType.TopLeft, SysCursorType.SizeNWSE, SysCursorType.SizeNWSE, ao));
            this.SubItemDict.Add(MovableItemType.Top, new EditableSubItem(this, MovableItemType.Top, SysCursorType.SizeNS, SysCursorType.SizeNS, ao));
            this.SubItemDict.Add(MovableItemType.TopRight, new EditableSubItem(this, MovableItemType.TopRight, SysCursorType.SizeNESW, SysCursorType.SizeNESW, ao));
            this.SubItemDict.Add(MovableItemType.Right, new EditableSubItem(this, MovableItemType.Right, SysCursorType.SizeWE, SysCursorType.SizeWE, ao));
            this.SubItemDict.Add(MovableItemType.BottomRight, new EditableSubItem(this, MovableItemType.BottomRight, SysCursorType.SizeNWSE, SysCursorType.SizeNWSE, ao));
            this.SubItemDict.Add(MovableItemType.Bottom, new EditableSubItem(this, MovableItemType.Bottom, SysCursorType.SizeNS, SysCursorType.SizeNS, ao));
            this.SubItemDict.Add(MovableItemType.BottomLeft, new EditableSubItem(this, MovableItemType.BottomLeft, SysCursorType.SizeNESW, SysCursorType.SizeNESW, ao));
            this.SubItemDict.Add(MovableItemType.Left, new EditableSubItem(this, MovableItemType.Left, SysCursorType.SizeWE, SysCursorType.SizeWE, ao));
        }
        protected void CalculateSubItemsBounds()
        {
            Rectangle b = this._Bounds;
            int s = ACTIVE_ITEM_SIZE;
            int x0 = 0;
            int x1 = b.Width / 2;
            int x2 = b.Width - 1;
            int y0 = 0;
            int y1 = b.Height / 2;
            int y2 = b.Height - 1;

            this.ItemArea.RelativeBoundsSetSize(x0, y0, b.Size);
            this.ItemTopLeft.RelativeBoundsSetCenter(x0, y0, s);
            this.ItemTop.RelativeBoundsSetCenter(x1, y0, s);
            this.ItemTopRight.RelativeBoundsSetCenter(x2, y0, s);
            this.ItemRight.RelativeBoundsSetCenter(x2, y1, s);
            this.ItemBottomRight.RelativeBoundsSetCenter(x2, y2, s);
            this.ItemBottom.RelativeBoundsSetCenter(x1, y2, s);
            this.ItemBottomLeft.RelativeBoundsSetCenter(x0, y2, s);
            this.ItemLeft.RelativeBoundsSetCenter(x0, y1, s);
        }
        protected EditableSubItem ItemArea { get { return this.SubItemDict[MovableItemType.Area]; } }
        protected EditableSubItem ItemTopLeft { get { return this.SubItemDict[MovableItemType.TopLeft]; } }
        protected EditableSubItem ItemTop { get { return this.SubItemDict[MovableItemType.Top]; } }
        protected EditableSubItem ItemTopRight { get { return this.SubItemDict[MovableItemType.TopRight]; } }
        protected EditableSubItem ItemRight { get { return this.SubItemDict[MovableItemType.Right]; } }
        protected EditableSubItem ItemBottomRight { get { return this.SubItemDict[MovableItemType.BottomRight]; } }
        protected EditableSubItem ItemBottom { get { return this.SubItemDict[MovableItemType.Bottom]; } }
        protected EditableSubItem ItemBottomLeft { get { return this.SubItemDict[MovableItemType.BottomLeft]; } }
        protected EditableSubItem ItemLeft { get { return this.SubItemDict[MovableItemType.Left]; } }
        /// <summary>
        /// Returns a (cached) array of currently active items (according by this.Style)
        /// </summary>
        /// <returns></returns>
        protected override IEnumerable<IInteractiveSubItem> SubItems
        {
            get
            {
                if (this.SubItemArray == null || this.LastStyle != this.Style)
                {
                    this.SubItemArray = this.SubItemDict.Values.Where(i => i.IsEnabled).ToArray();
                    this.LastStyle = this.Style;
                }
                return this.SubItemArray;
            }
        }
        protected Dictionary<MovableItemType, EditableSubItem> SubItemDict;
        protected IInteractiveSubItem[] SubItemArray;
        protected GInteractiveStyles LastStyle = GInteractiveStyles.None;
        protected enum MovableItemType : int
        {
            None = 0,
            Area,
            TopLeft,
            Top,
            TopRight,
            Right,
            BottomRight,
            Bottom,
            BottomLeft,
            Left
        }
        /// <summary>
        /// 2 = Active border (EditableSubItem.ActiveOverhead)
        /// </summary>
        protected const int ACTIVE_BORDER = 2;
        /// <summary>
        /// 5 = Active item size (size of grip)
        /// </summary>
        protected const int ACTIVE_ITEM_SIZE = 5;
        #endregion
        #region Sub-class MovableSubItem : class for sub-items in MovableArea
        /// <summary>
        /// MovableSubItem : class for sub-items in MovableArea
        /// </summary>
        protected class EditableSubItem : IInteractiveSubItem
        {
            public EditableSubItem(GEditableArea owner, MovableItemType itemType, SysCursorType? overCursorType, SysCursorType? dragCursorType, int activeOverhead)
            {
                this.Owner = owner;
                this.ItemType = itemType;
                this.OverCursorType = overCursorType;
                this.DragCursorType = dragCursorType;
                this.ActiveOverhead = activeOverhead;
            }
            public override string ToString()
            {
                return this.ItemType.ToString() + ": " + this.RelativeActiveBounds.ToString();
            }
            /// <summary>
            /// Owner of this Subitem = an EditableArea
            /// </summary>
            public GEditableArea Owner { get; private set; }
            /// <summary>
            /// Type of SubItem
            /// </summary>
            public MovableItemType ItemType { get; private set; }
            /// <summary>
            /// Cursor type in MouseOver state for this SubItem
            /// </summary>
            public SysCursorType? OverCursorType { get; private set; }
            /// <summary>
            /// Cursor type in MouseDrag state for this SubItem
            /// </summary>
            public SysCursorType? DragCursorType { get; private set; }
            /// <summary>
            /// Is this SubItem enabled by Owner.Style ?
            /// </summary>
            public bool IsEnabled { get { return this.Owner.IsEnabledSubItemType(this.ItemType); } }
            /// <summary>
            /// Can drag this SubItem
            /// </summary>
            public bool CanDrag { get { return (this.IsEnabled); } }
            /// <summary>
            /// Is this SubItem currently active SubItem of Owner?
            /// </summary>
            public bool IsCurrentItem { get { return (this.ItemType == this.Owner.CurrentSubItemType); } }
            /// <summary>
            /// Interactive State of this item
            /// </summary>
            public GInteractiveState ItemState { get { return (this.IsCurrentItem ? this.Owner.CurrentState : GInteractiveState.None); } }
            /// <summary>
            /// Overhead of active area outside visible bounds
            /// </summary>
            private int ActiveOverhead { get; set; }
            /// <summary>
            /// Current active area (only get), relative to Owner.Location.
            /// Area is settable by this.VisibleBounds.
            /// </summary>
            public Rectangle RelativeActiveBounds { get; private set; }
            /// <summary>
            /// Current visible area, absolute in Control.Location.
            /// </summary>
            public Rectangle VisibleBounds { get; private set; }
            /// <summary>
            /// Relative Active Bounds of sub item.
            /// IInteractiveSubItem liable member.
            /// </summary>
            Rectangle IInteractiveSubItem.RelativeActiveBounds { get { return this.RelativeActiveBounds; } }
            /// <summary>
            /// Is visible? true
            /// </summary>
            bool IInteractiveSubItem.IsVisible { get { return true; } }
            /// <summary>
            /// Hold a mouse attention.
            /// IInteractiveSubItem liable member.
            /// </summary>
            bool IInteractiveSubItem.HoldTheMouseAttention { get { return (this.ItemType != MovableItemType.Area); } }
            internal void RelativeBoundsSetSize(int x, int y, Size size)
            {
                this.ActivePoint = new Point(x, y);
                Rectangle rvb = new Rectangle(x, y, size.Width, size.Height);
                this.RelativeBoundsSet(rvb);
            }
            internal void RelativeBoundsSetCenter(int x, int y, int size)
            {
                this.ActivePoint = new Point(x, y);
                int half = size / 2;
                Rectangle rvb = new Rectangle(x - half, y - half, size, size);
                this.RelativeBoundsSet(rvb);
            }
            /// <summary>
            /// Store relative coordinates into this object:
            /// a) shift by this.Owner.Bounds.Location as VisibleBounds
            /// b) Inflate by ActiveOverhead and store as RelativeActiveBounds
            /// </summary>
            /// <param name="relativeBounds"></param>
            internal void RelativeBoundsSet(Rectangle relativeBounds)
            {
                this.VisibleBounds = relativeBounds.ShiftBy(this.Owner.Bounds.Location);
                if (this.ActiveOverhead > 0)
                    relativeBounds.Inflate(this.ActiveOverhead, this.ActiveOverhead);
                this.RelativeActiveBounds = relativeBounds;
            }
            internal Point ActivePoint { get; private set; }
        }
        #endregion
    }
}
